#!/usr/bin/env python
__version__ = "2.0.15"
__author__ = "Tarek Galal"

import sys, argparse, yowsup, logging
from yowsup.env import YowsupEnv

HELP_CONFIG = """
############# Yowsup Configuration Sample ###########
#
# ====================
# The file contains info about your WhatsApp account. This is used during registration and login.
# You can define or override all fields in the command line args as well.
#
# Country code. See http://www.ipipi.com/help/telephone-country-codes.htm. This is now required.
cc=49
#
# Your full phone number including the country code you defined in 'cc', without preceding '+' or '00'
phone=491234567890
#
# You obtain this password when you register using Yowsup.
password=NDkxNTIyNTI1NjAyMkBzLndoYXRzYXBwLm5ldA==
#######################################################
"""


CR_TEXT = """yowsup-cli  v{cliVersion}
yowsup      v{yowsupVersion}

Copyright (c) 2012-2016 Tarek Galal
http://www.openwhatsapp.org

This software is provided free of charge. Copying and redistribution is
encouraged.

If you appreciate this software and you would like to support future
development please consider donating:
http://openwhatsapp.org/yowsup/donate

"""

logger = logging.getLogger("yowsup-cli")


class YowArgParser(argparse.ArgumentParser):
    def __init__(self, *args, **kwargs):
        super(YowArgParser, self).__init__(*args, **kwargs)
        self.add_argument("-v", "--version",
            action = "store_true",
            help = "Print version info and exit"
        )

        self.add_argument("-d", "--debug",
            action = "store_true",
            help = "Show debug messages"
        )

        self.add_argument("--help-config", help = "Prints a config file sample", action = "store_true")
        self.args = {}
    def getArgs(self):
        return self.parse_args()

    def getConfig(self, config):
        try:
            f = open(config)
            out = {}
            for l in f:
                line = l.strip()
                if len(line) and line[0] not in ('#',';'):
                    prep = line.split('#', 1)[0].split(';', 1)[0].split('=', 1)
                    varname = prep[0].strip()
                    val = prep[1].strip()
                    out[varname.replace('-', '_')] = val
            return out
        except IOError:
            print("Invalid config path: %s" % config)
            sys.exit(1)

    def process(self):
        self.args = vars(self.parse_args())

        if self.args["debug"]:
            logging.basicConfig(level = logging.DEBUG)
        else:
            logging.basicConfig(level = logging.INFO)

        if self.args["version"]:
            print("yowsup-cli v%s\nUsing yowsup v%s" % (__version__, yowsup.__version__))
            sys.exit(0)

        if self.args["help_config"]:
            print(HELP_CONFIG)
            sys.exit(0)


    def printInfoText(self):
        print(CR_TEXT.format(cliVersion=__version__, yowsupVersion=yowsup.__version__))

class RegistrationArgParser(YowArgParser):
    def __init__(self, *args, **kwargs):
        super(RegistrationArgParser, self).__init__(*args, **kwargs)
        self.description = "WhatsApp Registration options"

        configGroup = self.add_argument_group("Configuration options",
                                              description = "Config file is optional. Other configuration arguments have higher priority if given, and will override those specified in the config file")

        configGroup.add_argument("-c", '--config',
                                 action = "store",
                                 help = 'Path to config file. If this is not set then you must set at least --phone and --cc arguments')

        configGroup.add_argument('-E', '--env',
                                  action = "store",
                                  help = "Set the environment yowsup simulates",
                                  choices = YowsupEnv.getRegisteredEnvs())

        configGroup.add_argument("-m", '--mcc',
                                 action = "store",
                                 help = "Mobile Country Code. Check your mcc here: https://en.wikipedia.org/wiki/Mobile_country_code")
        configGroup.add_argument("-n", '--mnc',
                                 action = "store",
                                 help = "Mobile Network Code. Check your mnc from https://en.wikipedia.org/wiki/Mobile_country_code")

        # configGroup.add_argument("-M", '--sim-mcc',
        #                          action = "store",
        #                          help = "Mobile Country Code. Check your mcc here: https://en.wikipedia.org/wiki/Mobile_country_code"
        #                          )
        # configGroup.add_argument("-N", '--sim-mnc',
        #                          action= "store",
        #                          help = "SIM MNC"
        #                          )

        configGroup.add_argument("-p", '--phone',
                                 action= "store",
                                 help = " Your full phone number including the country code you defined in 'cc', without preceeding '+' or '00'")
        configGroup.add_argument("-C", '--cc',
                                 action = "store",
                                 help = "Country code. See http://www.ipipi.com/networkList.do. This is now required")

        # configGroup.add_argument("-i", '--id',
        #                          action="store",
        #                          help = "Identity"
        #                          )

        regSteps = self.add_argument_group("Modes")
        regSteps = regSteps.add_mutually_exclusive_group()

        regSteps.add_argument("-r", '--requestcode', help='Request the digit registration code from Whatsapp.', action="store", required=False, metavar="(sms|voice)")
        regSteps.add_argument("-R", '--register', help='Register account on Whatsapp using the code you previously received', action="store", required=False, metavar="code")

    def process(self):
        super(RegistrationArgParser, self).process()

        config = self.getConfig(self.args["config"]) if self.args["config"] else {}

        if self.args["env"]:
            YowsupEnv.setEnv(self.args["env"])

        if self.args["mcc"]     : config["mcc"]     = self.args["mcc"]
        if self.args["mnc"]     : config["mnc"]     = self.args["mnc"]
        if self.args["phone"]   : config["phone"]   = self.args["phone"]
        if self.args["cc" ]     : config["cc"]      = self.args["cc"]
        #if self.args["sim_mnc"] : config["sim_mnc"] = self.args["sim_mnc"]
        #if self.args["sim_mcc"] : config["sim_mcc"] = self.args["sim_mcc"]

        if not "mcc"     in config: config["mcc"] = "000"
        if not "mnc"     in config: config["mnc"] = "000"
        if not "sim_mcc" in config: config["sim_mcc"] = "000"
        if not "sim_mnc" in config: config["sim_mnc"] = "000"




        try:
            assert self.args["requestcode"] or self.args["register"], "Must specify one of the modes -r/-R"
            assert "cc"      in config, "Must specify cc (country code)"
            assert "phone"   in config, "Must specify phone number"
        except AssertionError as e:
            print(e)
            print("\n")
            return False

        if not config["phone"].startswith(config["cc"]):
            print("Error, phone number does not start with the specified country code\n")
            return False

        config["phone"] = config["phone"][len(config["cc"]):]

        if self.args["requestcode"]:
            self.handleRequestCode(self.args["requestcode"], config)
        elif self.args["register"]:
            self.handleRegister(self.args["register"], config)
        else:
            return False

        return True

    def handleRequestCode(self, method, config):
        from yowsup.registration import WACodeRequest
        self.printInfoText()
        codeReq = WACodeRequest(config["cc"],
                                config["phone"],
                                config["mcc"],
                                config["mnc"],
                                config["mcc"],
                                config["mnc"],
                                method
        )
        result = codeReq.send()
        print(self.resultToString(result))

    def handleRegister(self, code, config):
        from yowsup.registration import WARegRequest
        self.printInfoText()
        code = code.replace('-', '')
        req = WARegRequest(config["cc"], config["phone"], code)
        result = req.send()
        print(self.resultToString(result))

    def resultToString(self, result):
        unistr = str if sys.version_info >= (3, 0) else unicode
        out = []
        for k, v in result.items():
            if v is None:
                continue
            out.append("%s: %s" %(k, v.encode("utf-8") if type(v) is unistr else v))

        return "\n".join(out)

class DemosArgParser(YowArgParser):
    def __init__(self, *args, **kwargs):
        super(DemosArgParser, self).__init__(*args, **kwargs)
        self.description = "Run a yowsup demo"

        configGroup = self.add_argument_group("Configuration options for demos")
        credentialsOpts = configGroup.add_mutually_exclusive_group()
        credentialsOpts.add_argument("-l", "--login", action="store", metavar="phone:b64password",
                                     help = "WhatsApp login credentials, in the format phonenumber:password, where password is base64 encoded.")
        credentialsOpts.add_argument("-c", "--config", action="store",
                                     help = "Path to config file containing authentication info. For more info about config format use --help-config")

        configGroup.add_argument('-E', '--env',
                                  action = "store",
                                  help = "Set the environment yowsup simulates",
                                  choices = YowsupEnv.getRegisteredEnvs())

        configGroup.add_argument("-M", "--unmoxie", action="store_true", help="Disable E2E Encryption")

        cmdopts = self.add_argument_group("Command line interface demo")
        cmdopts.add_argument('-y', '--yowsup', action = "store_true", help = "Start the Yowsup command line client")

        echoOpts = self.add_argument_group("Echo client demo")
        echoOpts.add_argument('-e', '--echo', action = "store_true", help = "Start the Yowsup Echo client")

        sendOpts = self.add_argument_group("Send client demo")
        sendOpts.add_argument('-s', '--send', action="store", help = "Send a message to specified phone number, "
                                                                     "wait for server receipt and exit",
                              metavar=("phone", "message"), nargs = 2)
        syncContacts = self.add_argument_group("Sync contacts")
        syncContacts.add_argument('-S','--sync', action = "store" , help = "Sync ( check valid ) whatsapp contacts",metavar =("contacts"))

    def process(self):
        super(DemosArgParser, self).process()

        if self.args["env"]:
            YowsupEnv.setEnv(self.args["env"])

        if self.args["yowsup"]:
            self.startCmdline()
        elif self.args["echo"]:
            self.startEcho()
        elif self.args["send"]:
            self.startSendClient()
        elif self.args["sync"]:
            self.startSyncContacts()
        else:
            return False
        return True

    def _getCredentials(self):
        if self.args["login"]:
            return tuple(self.args["login"].split(":"))
        elif self.args["config"]:
            config = self.getConfig(self.args["config"])
            assert "password" in config and "phone" in config, "Must specify at least phone number and password in config file"
            return config["phone"], config["password"]
        else:
            return None

    def startCmdline(self):
        from yowsup.demos import cli
        credentials = self._getCredentials()
        if not credentials:
            print("Error: You must specify a configuration method")
            sys.exit(1)
        self.printInfoText()
        stack = cli.YowsupCliStack(credentials, not self.args["unmoxie"])
        stack.start()

    def startEcho(self):
        from yowsup.demos import echoclient
        credentials = self._getCredentials()
        if not credentials:
            print("Error: You must specify a configuration method")
            sys.exit(1)
        try:
            self.printInfoText()
            stack = echoclient.YowsupEchoStack(credentials, not self.args["unmoxie"])
            stack.start()
        except KeyboardInterrupt:
            print("\nYowsdown")
            sys.exit(0)

    def startSendClient(self):
        from yowsup.demos import sendclient
        credentials = self._getCredentials()
        if not credentials:
            print("Error: You must specify a configuration method")
            sys.exit(1)

        try:
            self.printInfoText()
            stack = sendclient.YowsupSendStack(credentials, [([self.args["send"][0], self.args["send"][1]])],
                                               not self.args["unmoxie"])
            stack.start()
        except KeyboardInterrupt:
            print("\nYowsdown")
            sys.exit(0)

    def startSyncContacts(self):
        from yowsup.demos import contacts
        credentials = self._getCredentials()
        if not credentials:
            print("Error: You must specify a configuration method")
            sys.exit(1)
        try:
            self.printInfoText()
            stack = contacts.YowsupSyncStack(credentials,self.args["sync"].split(','), not self.args["unmoxie"])
            stack.start()
        except KeyboardInterrupt:
            print("\nYowsdown")
            sys.exit(0)


if __name__ == "__main__":
    args = sys.argv
    if(len(args) > 1):
        del args[0]


    modeDict = {
        "demos":        DemosArgParser,
        "registration": RegistrationArgParser,
        "version":      None
    }

    if(len(args) == 0 or args[0] not in modeDict):
        print("Available commands:\n===================")
        print(", ".join(modeDict.keys()))

        sys.exit(1)

    mode = args[0]
    if mode == "version":
        print("yowsup-cli v%s\nUsing yowsup v%s" % (__version__, yowsup.__version__))
        sys.exit(0)
    else:
        parser = modeDict[mode]()
        if not parser.process():
            parser.print_help()
